啊呃,果然現實是殘酷的,原本今天想開始練習Active Record 的query methods,但很不幸地是剛研究沒多久就碰壁啦!
猜猜為什麼?
irb(main):001:0> Post.first.user
Post Load (0.3ms) SELECT "posts".* FROM "posts" ORDER BY "posts"."id" ASC LIMIT $1 [["LIMIT", 1]]
Traceback (most recent call last):
1: from (irb):001
NoMethodError (undefined method `user' for #<Post:0x00007f9cec456790>)
Did you mean? userId
是的,我忘了加上關聯!看到這訊息馬上就知道是少了associations 的方法(可回顧Day 6-8 的文章,),那我們就加上關聯吧:
class Post < ApplicationRecord
belongs_to :user
end
# 加了後:
irb(main):002:0> Post.first.user
Post Load (0.2ms) SELECT "posts".* FROM "posts" ORDER BY "posts"."id" ASC LIMIT $1 [["LIMIT", 1]]
nil # -> nilllllll!?
嗯?為什麼?XD
我們來確定一下Post 的欄位:
create_table "posts", force: :cascade do |t|
t.integer "userId"
t.text "title"
t.text "body"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end # -> 看起來也沒問題呀!?
# 看一下attributes 的資訊
irb(main):003:0> Post.first.userId
Post Load (0.8ms) SELECT "posts".* FROM "posts" ORDER BY "posts"."id" ASC LIMIT $1 [["LIMIT", 1]]
1
irb(main):004:0> Post.first.userId.class
Post Load (0.4ms) SELECT "posts".* FROM "posts" ORDER BY "posts"."id" ASC LIMIT $1 [["LIMIT", 1]]
Integer < Numeric # -> 嗯?形態、資料都對,可那為什麼咧?
讓我們來試試.joins(:user)
:
irb(main):008:0> Post.joins(:user)
Post Load (3.1ms) SELECT "posts".* FROM "posts" INNER JOIN "users" ON "users"."id" = "posts"."user_id"
Traceback (most recent call last):
ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column posts.user_id does not exist)
LINE 1: ...FROM "posts" INNER JOIN "users" ON "users"."id" = "posts"."u...
^
HINT: Perhaps you meant to reference the column "posts.userId". # !!!!!!
看到這可發現,Rails 已善意地提醒我們了:加上belongs_to 後,黑魔法預設幫我們設定foreign_key 為posts.user_id
,可因為我們的欄位名稱是userId
,那怎麼辦呢?
但因為1 可能會影響昨天的懶人seeding 法,所以我比較想嘗試2 的方法:
class Post < ApplicationRecord
belongs_to :user, foreign_key: :userId
end
irb(main):010:0> Post.first.user
Post Load (0.7ms) SELECT "posts".* FROM "posts" ORDER BY "posts"."id" ASC LIMIT $1 [["LIMIT", 1]]
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
#<User:0x00007f9ceabc91a0> {
:id => 1,
:created_at => Mon, 26 Sep 2022 13:45:53 UTC +00:00,
:updated_at => Mon, 26 Sep 2022 13:45:53 UTC +00:00,
:name => "Leanne Graham",
:username => "Bret",
:email => "Sincere@april.biz",
:phone => "1-770-736-8031 x56442",
:website => "hildegard.org"
}
成功!
那我們就知道能用指定foreign_key 的方法處理belongs_to :user
(posts, albums, todos) 的資料表
可如果反過來呢?我們是否能用User.first.posts
來看has_many 的posts 呢:
# 同樣的,需要先在User 加上has_many :post,但事與願違:
irb(main):003:0> User.first.posts
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1 [["LIMIT", 1]]
Traceback (most recent call last):
ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column posts.user_id does not exist)
LINE 1: SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = $1
^
HINT: Perhaps you meant to reference the column "posts.userId". # !!!!?!?
可神奇的是:
class User < ApplicationRecord
has_many :posts, foreign_key: :userId
end
# 就能找到資料啦!
irb(main):010:0> User.first.posts
User Load (0.5ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1 [["LIMIT", 1]]
Post Load (0.7ms) SELECT "posts".* FROM "posts" WHERE "posts"."userId" = $1 [["userId", 1]]
[
[0] #<Post:0x00007f9ceac1e920> {
:id => 1,
:userId => 1,
:title => "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
:body => "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto",
:created_at => Mon, 26 Sep 2022 13:45:30 UTC +00:00,
:updated_at => Mon, 26 Sep 2022 13:45:30 UTC +00:00
},
不知道有沒有魯魯跟我一樣好奇,或以為在User Model 加上的foreign_key 應該是指自己的attributes/columns?
可其實不然的喲~~
我們來看看APIdock 怎麼寫:
foreign_key public
Creates a foreign key name from
a class name. (就是這個from!
) separate_class_name_and_id_with_underscore sets whether the method should put ‘_’ between the name and ‘id’.
'Message'.foreign_key # => "message_id"
'Message'.foreign_key(false) # => "messageid"
'Admin::Post'.foreign_key # => "post_id"
讓我們在console 試試:
irb(main):006:0> 'Post'.foreign_key
"post_id"
irb(main):007:0> 'User'.foreign_key
"user_id"
可以看到convention 上其實都將foreign_key 視為classname_id 的啦,難怪我們需要特別指定了~~
不過當然我們也能這麼做:
class User < ApplicationRecord
has_many :posts, foreign_key: 'userId'
end
如此也是行得通的囉!
那今天的任務就是把所有Model 和tables 的關聯設定好再睡!今天就這麼廢下去好了,謝謝大家~~